Skip to content

fix(workflow): tell a node the OUTPUTs its edges read - #2670

Merged
Birfy merged 1 commit into
mainfrom
dev/2669-edge-outputs
Nov 8, 2026
Merged

Birfy merged 1 commit into
mainfrom
dev/2669-edge-outputs

Conversation

@Birfy

@Birfy Birfy commented Nov 8, 2026 •

Copy link
Copy Markdown
Collaborator

Written-by: Dev

Fixes #2669

Owner: Dev
Reviewer: Reviewer

What changed

stepBody() hands each step its declared outputs plus the outputs its outgoing edges read (edgeOutputs in src/graph.js, next to missingVerdicts), and the handoff spells the values an edge compares against: OUTPUT state: <one of: ready>. missingVerdicts is untouched — a step that prints nothing still fails the gate; it is just no longer asked for a line it was never told to print. Docs: one sentence in docs/subsystems/workflow.md §5, fragment changelog.d/2669-edge-outputs.fixed.md.

Red first

At 3b8d4e8c the new tests fail (plugins/workflow, exit 1, pass 33 / fail 4):

not ok 2  - test/graph.test.js  SyntaxError: The requested module '../src/graph.js' does not provide an export named 'edgeOutputs'
not ok 34 - #2669: a node that declares no outputs is handed the ones its edges read   (outputs.state === undefined)
not ok 35 - #2669: the handoff names the OUTPUT and the values it may take
            Input: 'Task i2669-impl: Triage the issue.\nReply with the result of this step.'
not ok 36 - #2669: declared outputs are kept once, with the values their edges read   (['state','summary'] vs +'verdict')

Ran at this tip: 6c9eca5

The working copy the runs used was fetched back file by file from this ref and matches (7/7); the ship step re-fetched the same seven files at the tip and re-ran the brief's four-file set.

$ cd plugins/workflow && TZ=UTC DSH_HARNESS=/data/dsh/bot/workspace/d2607/repo/core/dev/harness node --import ./test/resolve.mjs --test test/step-body.test.js test/graph.test.js test/assign.test.js test/queue.test.js
  -> exit 0, # tests 47 / # pass 47 / # fail 0

$ cd plugins/workflow && TZ=UTC DSH_HARNESS=/data/dsh/bot/workspace/d2607/repo/core/dev/harness node --import ./test/resolve.mjs --test "test/*.test.js"
  -> exit 0, # tests 475 / # pass 475 / # fail 0

$ node .github/scripts/changelog.mjs check
  -> 21 fragment(s) in changelog.d/ are well formed, exit 0

Not run

  • npm run verify — the brief runs one suite at a time on this box, and no core/ file is in this diff.
  • build-client.mjs --check / ds-bot-patches.mjs check — no core/ change.
  • plugin oxlint and the arch-docs check — no oxlint binary and no git on this box; the owned page docs/subsystems/workflow.md is updated in the same commit.

Out of scope (follow-ups, not touched here): missingVerdicts also reads a loop edge's until.output, and readStepResult still drops an OUTPUT line a node prints for a name outside its declared ports.

Checkpoint

{
  "pr": 2670, "claim": "Dev", "issue": 2669,
  "summary": "A workflow node was only told the OUTPUTs it declared, while its outgoing edges read OUTPUTs too, so the issue fast line's Triage node never learned to print `state` and runs failed on `Missing OUTPUT state`. stepBody now hands the declared ports plus the outputs the outgoing edges read, and the handoff spells the values an edge compares against. missingVerdicts and the fail path are unchanged.",
  "files": ["plugins/workflow/src/graph.js", "plugins/workflow/src/service.js", "plugins/workflow/index.js", "plugins/workflow/test/step-body.test.js", "plugins/workflow/test/graph.test.js", "docs/subsystems/workflow.md", "changelog.d/2669-edge-outputs.fixed.md"],
  "checks": { "ran": "plugins/workflow: node --test test/*.test.js and the four-file brief set; changelog.mjs check", "result": "green", "at": "6c9eca56cca0cf08571a368af6c7ee74db08e4ad" },
  "next": "none — ready for review"
}

A node was only handed the outputs it declared, while the outgoing edges read
OUTPUTs too — so a graph whose Triage declares none and whose edges branch on
`state` never told the node to print that line, and every run of that shape
failed on `Missing OUTPUT state`.

`stepBody` now hands the declared ports plus the outputs its edges read
(`edgeOutputs`, next to `missingVerdicts`), and the handoff spells the values
an edge compares against, so a step can answer the gate it is judged by.

Written-by: Dev
@Birfy Birfy added the agent-pr Pull request opened by an agent label Nov 8, 2026
@Birfy Birfy self-assigned this Nov 8, 2026
@Birfy

Birfy commented Nov 8, 2026

Copy link
Copy Markdown
Collaborator Author

Verdict: PASS at 6c9eca5
Written-by: Reviewer
Attestation: 6c9eca5 role=Reviewer

Ran at this tip:

$ cd plugins/workflow && node --import ./test/resolve.mjs --test "test/*.test.js"
# tests 475  # pass 475  # fail 0

$ cd plugins/workflow && cp /data/dsh/bot/workspace/p2670/graph.base.js src/graph.js && node --import ./test/resolve.mjs --test test/step-body.test.js test/graph.test.js
# tests 2  # pass 0  # fail 2   (both files abort: graph.js provides no export named 'edgeOutputs')

$ cd plugins/workflow && node -e "const fs=require('fs');let s=fs.readFileSync('src/graph.js','utf8');const a=s.indexOf('export function edgeOutputs');const b=s.indexOf('\n}',a);fs.writeFileSync('src/graph.js',s.slice(0,a)+'export function edgeOutputs(graph, nodeId) { ensure(graph); return [] }'+s.slice(b))" && node --import ./test/resolve.mjs --test test/step-body.test.js test/graph.test.js
# tests 15  # pass 11  # fail 4

$ cd plugins/workflow && cp /data/dsh/bot/workspace/p2670/graph.tip.js src/graph.js && node --import ./test/resolve.mjs --test "test/*.test.js"
# tests 475  # pass 475  # fail 0

Red first, both ways. Reverting plugins/workflow/src/graph.js to the base blob 3b8d4e8c (new tests kept) makes both affected files red (exit 1, 0 pass / 2 fail; the module has no edgeOutputs). Keeping the export but reverting its behaviour (edgeOutputs → []) reds the four new tests: 11 pass / 4 fail — the node gets no state, the handoff never says OUTPUT state / one of: ready, the declared-port merge and the branch walk fail. Restored at the tip: 475/475 green.

审查要点:

  • edgeOutputs(item, nodeId): only from === nodeId and when.output; when.else contributes nothing; values deduped in edge order (ready, blocked); no values for an edge without eq. Covered by test/graph.test.js (['ready','blocked'], else skipped, unknown node → []).
  • stepBody uses declared ports ∪ edges' read ports, declared wins (type: text on summary), same-name values merged; test/step-body.test.js asserts ['state','summary','verdict'] with no duplicate and state.values = ['ready'].
  • index.js: with values it writes OUTPUT state: <one of: ready>; without values it keeps the single OUTPUT <name>: <value> form. Real graph wf-1cdf6ec5 through createWorkflowService yields When you finish, add one line per output: OUTPUT state: <one of: ready>, OUTPUT issue: <value>, … — triage gets state and the prompt carries ready.
  • missingVerdicts and the fail path are untouched (no hunk touches them): a step that prints nothing still fails; it is only told which line to print.
  • Scope: exactly 7 files (plugins/workflow/**, docs/subsystems/workflow.md, changelog.d/2669-edge-outputs.fixed.md). No template.js, no define-time validation/rejection, no partial settlement; the only change to an old test file is the import line (edgeOutputs), no assertion is weakened.
  • check-runs at this tip: everything terminal and green/skipped except enqueue-guard (no Verifier report names the tip 6c9eca56), which this PASS answers. No hold; agent-pr present; body carries Fixes #2669, Owner: Dev, Reviewer: Reviewer; checkpoint block valid (1019 chars, fields ok, checks.at is the full tip).

@github-actions github-actions Bot added the pass label Nov 8, 2026
@Birfy
Birfy added this pull request to the merge queue Nov 8, 2026
Merged via the queue into main with commit 656fada Nov 8, 2026
18 of 20 checks passed
@Birfy
Birfy deleted the dev/2669-edge-outputs branch November 8, 2026 05:36
@github-actions github-actions Bot removed the agent-pr Pull request opened by an agent label Nov 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug(workflow): a node is never told which OUTPUT its outgoing edges read

1 participant